home *** CD-ROM | disk | FTP | other *** search
Wrap
/****************************************************************************** MODUL alias.c DESCRIPTION macros for DME/XDME NOTES a part of that path is in command.c BUGS <none known> TODO -/- EXAMPLES -/- SEE ALSO command.c INDEX HISTORY <see RCS-File> ******************************************************************************/ /* ** (C)Copyright 1992 by Bernd Noll for null/zero-soft ** All Rights Reserved ** ** RCS Header: $Id: alias.c,v 1.65 92/11/09 12:47:29 b_noll Exp $ ** ** compiling with ** "#define PATCH_NULL" ** "#define PATCH_MACROS" ** causes that module to be used ** *!********************************************************************* *! *! MACROS - OVERVIEW ** ** this option allows translation of alias.c ** and enables a change in do_command() to allow macro-execution ** and (perhaps) one in main() to initialize the macro-lists *! *! the module supports several commands: *! SETMACRO definition of a new macro *! UNSETMACRO deletion of a macro *! *! SETMACROVAR definition of a macro's local variable *! UNSETMACROVAR deletion of suh a variable *! (at the end of a macrocall all its local variables *! are deleted automatically) *! *********************************************************************** ** ** Implementation: ** ** At the moment macros are stored in MACRO_NUM Lists, which ** are selected with the 1st char of the macro's name ** to speed up search-operations ** ** All names are switched to lowercase, so there should be ** no case sensitivity ** ** please note, that there is acess to "av[]" and to several functions of ** the module "varsbases" in this module, so porting to another program ** than DME might need some changes. ** */ /************************************** Includes **************************************/ #define MACRO struct MacroNode /* trick 17 * OPAQUE */ #include "defs.h" #include "AVL.h" /************************************** Interne Defines & Struktures **************************************/ /* ** U might use MACRO like an opaque type */ struct MacroNode { struct Node Node; /* includes a ptr to the macro-name */ char* Body; /* statements to be executed at a macro-call (this all must fit in one line) */ short NArgs; /* 0..7 number of arguments to be given from do_command */ short NFlags; /* PERHAPS FUTURE: 0..n number of local-flags to be supported */ char freeme; /* NOT USED ANY MORE 0/1 flag if the macro is builtin or user-defined */ char killme; /* 0/1 flag if he maxro shall be deleted after termination of its last macrocall */ short NCalls; /* it seems to be necessary to forbid all deletions of an active macro */ }; struct MacroArg { struct MinNode ma_Node; /* hook for recursion-stack */ struct MacroNode*ma_Macro; /* a reference to the called macro w/ all its data */ VarRoot ma_Locals; /* AVL-Tree or List of Local Variables */ /* we could also have a ptr to an array of ptrs instead of a list, but then it might be necessary to use one more parameter for SETMACRO */ char* ma_Arguments[2];/* array of ptr to string; there must always be at least two entries: a 1st one referencing the macroname and a last one == NULL */ }; /************************************** Prototypes & Exports **************************************/ Prototype void do_setmacro (void); Prototype void do_unsetmacro (void); Prototype void do_setmacrovar (void); Prototype void do_unsetmacrovar(void); Prototype void do_retmacro (void); Prototype void do_macrosave (void); Prototype void do_macroload (void); Prototype void SetMacroVar (char* name, char* value); Prototype MACRO*getmacro (char* name); Prototype int callmacro (MACRO*); Prototype int nummacroargs (MACRO*); Prototype char* getmacroarg (char* name); Prototype char* getmacrovar (char* name); Prototype void DelMacros (void); Prototype int SetMacro (char* name, char* body, int nargs); Prototype int savemacros (FILE *); /* PATCH_NULL [25 Jan 1993] : proto'd for pack.c */ Prototype int loadmacros (FILE *, int *); /* PATCH_NULL [25 Jan 1993] : proto'd for pack.c */ Prototype int GetDefMacros (void); /* PATCH_NULL [25 Jan 1993] : proto'd for pack.c */ /* ** usually I use a global Struct that contains all important ** variables - so the Macrolists too, that is to allow ** switching between several environments in future ** if that global structure is not used, we can support our ** own - local - variable, else we must access the lists via a ** macro. ** the argstack is not touched by that division, as command flow cannot ** be splitted ** !WARNING! it CAN be splitted when using asynchroneous Rexx-ports ** but when writing that module, i did not intend to ** use really async rexx-commands ** ** NOTE */ /************************************** Internal Variables **************************************/ /* ** *argstack contains all data which is created during one macro-call ** *delenda is a delete-buffer for all macros, will have been removed ** from their lists, but cannot deleted, as there is a prior call to ** not returned; they are deleted then aftermacro-termination or ** in exit_macros. */ static struct TreeNode* macros = NULL; /* ... OF MACRO */ static struct TreeNode* delenda= NULL; /* ... OF MACRO */ static MLIST argstack; /* ... OF struct MacroArg */ char MacroBreak = 0; /* internal possibility to terminate a macro */ /* PATCH_NULL [08 Mar 1993] : removed "static" */ /* extern int count_tree (APTR); */ /********************************************** ** ** Definition and deletion of Macros ** **********************************************/ /* ** FreeMacro() ** DelMacro() ** 2 help-Functions of do_unsetmacro (and exit_macros) ** ** DelMacro() should delete only Macros, which are not in use ** for that reason it checks usecount, and if its not 0, the ** macro is put into another list and marked to be killed by ** callmacro() after execution. */ static void FreeMacro(MACRO* m) { if (m->freeme) { /* if that macro is not a builtin one, */ free(m->Node.ln_Name); /* free it and its contents */ free(m->Body); free(m); } /* if */ } /* FreeMacro */ static void DelMacro(char* name) { MACRO* m; if ((m = getmacro(name))) { /* find the node representing the macro ... */ /* printf ("D:%s\n", name); */ AVL_Remove (¯os, (APTR)m); /* , remove it from its tree ... */ if (!m->NCalls) { /* and if it is not in use */ FreeMacro(m); /* ... free its contents */ } else { /* ... else put it into a certain kill-list */ m->killme++; /* ... and mark it to be killed */ AVL_Append (&delenda, (APTR)m); } /* if */ } /* if */ } /* DelMacro */ /* ** SetMacro() ** a help-Function of do_setmacro (and maybe init_macros) ** (it is also called by packages.mergpack) ** which creates a new macro-node and fills copies of the parameters ** */ int SetMacro(char* name, char* body, int nargs) { MACRO* m; if (nargs >= NARGS) { error ("too many arguments specified for macro"); return(0); } /* if too many args */ DelMacro(name); /* for sure: remove all macros of the same name */ if ((m = malloc(sizeof(MACRO)))) { /* allocate a macro-structure... */ setmem(m, sizeof(MACRO),0); if ((m->Node.ln_Name = strdup(name))) { /* ... the name ... */ if ((m->Body = strdup(body))) { /* ... and the body, */ m->NArgs= nargs; /* fill in all values, */ m->freeme = 1; AVL_Append (¯os, (APTR)m); /* and append the structure to a certain macro-list */ return(1); } /* if block copied */ free(m->Node.ln_Name); /* if anything went wrong */ } /* if name copied */ /* free all allocated frags */ free(m); /* and set an error */ } /* if struct allocated */ nomemory(); return(0); } /* SetMacro */ /********************************************** ** ** Access to Macros' arguments and local variables ** **********************************************/ /* ** getmacrovar() ** get a local variable of the current(== last) macrocall ** we are supposing that only one process has access to resources ** that function is meant to be interface for getvar() */ char* getmacrovar(char* name) { struct MacroArg* n = NULL; if (!(n = GetHead(&argstack))) /* PATCH_NULL [03 Apr 1993] */ { /* this function can not work if not called from within a macrocall */ return(NULL); } /* if */ return(GetVarFrom (&n->ma_Locals, name)); } /* getmacrovar */ /* ** SetMacroVar() ** a help-function of do_setmacrovar() ** and an interface to settypedvar() */ void SetMacroVar(char* name, char* value) { struct MacroArg* n = NULL; if (!(n = GetHead(&argstack))) { /* this function can not work if not called from within a macrocall */ abort2(); } /* if */ SetVarInto (&n->ma_Locals, name, value); } /* SetMacroVar */ /* ** getmacroarg() ** get an parameter of the current(== last) macrocall ** we are supposing that only one process has access to resources ** so we only have to check the topmost element of argstack *! NOTE: ARGUMENTS to a macro are READ-ONLY, *! You can't alter their values from the body of the called macro *! */ char* getmacroarg(char* name) { struct MacroArg* n = NULL; int num; char* res; if (!(n = GetHead(&argstack))) { /* this function can not work if not called from within a macrocall */ return(NULL); } /* if */ if ((strncmp(name,"arg",3) != 0) || (!is_number(name+3))) { /* and it responds only to names of the type "ARGx" with x in /N */ return(NULL); } /* if */ num = atoi(name+3); if (n->ma_Macro->NArgs>=num) { /* if the last called function has an arg ARGx */ if ((res = strdup (n->ma_Arguments[num]))) { /* and U can duplicate it, then return the copy */ return(res); } else { nomemory(); abort(NULL); } /* if (not) malloced res */ } else { abort(NULL); } /* if (not) exists argX */ } /* getmacroarg */ /********************************************** ** ** Usage of Macros ** ** for calls from do_command the sequence must ** be like that: ** ** if (m=getmacro) then ** for i=1 to nummacroargs(m) do ** put nextarg to av[i] ** od; ** callmacro(m); ** fi; ** ** callmacro() pushes av[i] onto argstack and ** clears them, then locks the macro and ** calls do_command with a copy of the ** macro's block ** when the execution returns, av[i] are ** restored and topof argstack is cleared ** **********************************************/ /* ** callmacro() ** Call a Macro as interface from do_command() ** (first you must have got a "handle" via getmacro()) */ int callmacro(MACRO* m) { struct MacroArg* n = NULL; char** args = NULL; char* com = NULL; int ret = 0; int i; int len = m->NArgs*4+sizeof(struct MacroArg); n = (struct MacroArg*)malloc(len); /* allocate space for the stack-element... */ com = strdup(m->Body); /* and duplicate the macro's body */ if (n && com) { setmem(n,len,0); /* clear all data */ AddHead((LIST*)&argstack,(NODE*)n); /* and push the struct to argstack */ InitVars (&n->ma_Locals); /* for use with encapsulated local variables we must initialize their list */ av[0] = m->Node.ln_Name; /* define argslot 0 - it might be asked during execution, and perhaps it was not set in do_command */ args = n->ma_Arguments; /* then put all arguments into the struct */ for (i = m->NArgs; i >= 0; i--) { /* and clear their global values */ args[i] = av[i]; av[i] = NULL; } /* for */ /* [25 Jan 1993] : line added */ n->ma_Macro = m; m->NCalls++; /* disable deletion of that macro during execution */ MacroBreak = 0; /* clear the return - flag */ ret = do_command(com); /* and call the macrobody (execute only the copy, as do_command might destroy the data) */ if (MacroBreak) { ret = 1-ret; globalflags.Abortcommand = MacroBreak-1; MacroBreak = 0; } /* if */ for (i = m->NArgs; i >= 0; i--) { /* after the call restore the global values of all args, ... */ av[i] = args[i]; } /* for */ m->NCalls--; /* and enable deletion of that macro after execution */ if (m->NCalls == 0 && m->killme) { /* if we must delete the macro, do it */ AVL_Remove(&delenda, (APTR)m); FreeMacro(m); } /* if */ DelAllVarsFrom (&n->ma_Locals); /* delete all local variables ... */ Remove((NODE*)n); /* and pop the margs-struct from argstack */ } else { nomemory(); } /* if */ if (n) /* and free anything you allocated */ free(n); if (com) free(com); return(ret); } /* callmacro */ /* ** getmacro() ** ** Find a Function of a given Name ** so macros can be tested for existance and user can get a ptr to a "Handle" ** that function is part of the interface to do_command() */ MACRO* getmacro(char* name) { MACRO* m; m = (MACRO*)AVL_Find (¯os, name); return(m); } /* getmacro */ #if 0 /* ** That function might be used as an interface for getvar ** to access the contents of a macro */ char* GetFuncBody(char* name) { MACRO* m = NULL; char* str = NULL; if (m = (MACRO*)getmacro(name)) { str = strdup(m->Body); } /* if */ return(str); } /* GetFuncBody */ #endif /* NOT_DEF */ /* ** nummacroargs() ** Get the Number of Arguments of a Macro ** that function is used by do_command to get informations ** how many argslots to fill */ int nummacroargs(MACRO* m) { return(m->NArgs); } /* nummacroargs */ /********************************************** ** ** Initial Access to Macros ** ** **********************************************/ /* ** resmacros ** that structure shows all builtin macros ** which are defined at program start ** they are initialized via GetDefMacros */ typedef struct _mres { char * name; char * body; int numargs; } MRES; static CONST MRES resmacros[] = { /* name, block, args */ { "alias", "setmacro $arg1 0 $arg2", 2 }, { "aslload", "arpload", 0 }, { "aslinsfile", "arpinsfile", 0 }, { "aslfont", "arpfont", 0 }, { "aslsave", "arpsave", 0 }, /* { "asl", "arp", 0 }, */ { "unalias", "unsetmacro $arg1", 1 }, { "firstwindow", "select f", 0 }, { "lastwindow", "select l", 0 }, { "nextwindow", "select n", 0 }, { "prevwindow", "select p", 0 }, { "so", "if m saveold", 0 }, { NULL, NULL, 0 } }; /* resmacros */ /* ** GetDefMacros ** initialize the builtin default-macros */ int GetDefMacros (void) { int i; for (i = 0; resmacros[i].name; i++) { if (!SetMacro(resmacros[i].name, resmacros[i].body, resmacros[i].numargs)) { return (0); /* PATCH_NULL [25 Jan 1993] : line added */ } /* if error */ /* PATCH_NULL [25 Jan 1993] : line added */ } /* for all builtins */ return (1); } /* GetDefMacros */ /* That static variable is needed to communicate between savesinglemacro and savemacros */ static FILE * mfi; void savesinglemacro (MACRO * m) { if (m && mfi) { fprintf (mfi, "\tMACRO %s\n", m->Node.ln_Name); if (m->NArgs) { fprintf (mfi, "\t ARGS %d\n", m->NArgs); } /* if */ fprintf (mfi, "\t BODY %s\n", m->Body); } /* if */ } /* savesinglemacro */ int savemacros (FILE * fo) { fprintf(fo, "MACROS START\n"); mfi = fo; AVL_ScanTree (¯os, (APTR)savesinglemacro, 1); fprintf(fo, "MACROS END\n"); return (1); } /* savemacros */ int loadmacros (FILE * fi, int * lineno) { char * buf; char nmacro [128]; char nargs [128]; char body [2*LINE_LENGTH]; buf = getnextcomline(fi, lineno); /* printf("read %s\n", buf); */ if (!buf) { abort(0); } /* if */ if (strncmp(buf, "MACROS START", 12) != 0) { error ("No Macrostart header"); abort(0); } /* if */ nmacro[0] = 0; nargs [0] = 0; body [0] = 0; while (globalflags.Abortcommand == 0) { buf = getnextcomline(fi, lineno); /* printf("read %s\n", buf); */ if (!buf) { abort(0); } /* if */ if (strncmp(buf, "MACROS END", 10) == 0) { return(1); } else if (strncmp(buf, "MACRO ", 6) == 0) { buf += 6; strncpy(nmacro, buf, 128); /* printf ("\nMACRO:", buf, 1); */ /* printf (buf, 1); */ } else if (strncmp(buf, "ARGS ", 5) == 0) { buf += 5; if (nargs[0] != 0) { error("%s:\nDeclared numargs not w/in a Macro", av[0]); abort(0); } /* if */ strncpy(nargs, buf, 128); } else if (strncmp(buf, "BEGIN", 5) == 0) { int len = 0; buf += 5; while (*buf && *buf<33) buf++; if (nmacro[0] == 0) { error("<%s:\nDeclared Body w/out Macro", av[0]); abort(0); } /* if */ if (*buf) { len = strlen(buf); strcpy (body, buf); } /* if */ while ((!globalflags.Abortcommand) && (buf = getnextcomline(fi, lineno))) { char * inter = &body[len]; /* printf ("reading %s\n", buf); */ if (strncmp (buf, "END", 3) == 0) { /* printf ("setting %s\n", body); */ goto setit; } /* if */ len += strlen(buf)+1; if (len < 2*LINE_LENGTH) { *inter = ' '; strcpy(inter+1, buf); } else { error ("%s:\nmacro-body too long", av[0]); } /* if */ } /* while */ goto retfail; } else if (strncmp(buf, "BODY", 4) == 0 && (buf[4] < 0x21)) { /* printf ("B:%s\n", buf + 4); */ buf += 4; if (*buf) buf++; if (nmacro[0] == 0) { error("<%s:\nDeclared Body w/out Macro", av[0]); abort(0); } /* if */ strncpy(body, buf, 2*LINE_LENGTH); setit: av[1] = nmacro; av[2] = nargs[0] ? nargs : "0"; av[3] = body; /* printf (";", nmacro); */ do_setmacro(); nmacro[0] = 0; nargs [0] = 0; } else { error ("%s:\nunknown identifier '%s'", av[0], buf); } /* if types */ } /* while not ready */ retfail: return(0); } /* loadmacros */ /*************************************************** COMMAND INTERFACE ***************************************************/ /* *! >SETMACROVAR name value *! >SMV name value *! define a local variable for the current macro-call *! all local vars will be deleted at the end of a macro-execution *! (SMV is a shortcut for SETMACROVAR as we can't define a short *! macro for that command) *! */ void do_setmacrovar() { SetMacroVar(av[1], av[2]); } /* do_setmacrovar */ /* *! >UNSETMACROVAR name *! drop a local variable before the end of the current macro-call *! all local vars will be deleted at the end of a macro-execution *! so that command may be obsolete *! */ void do_unsetmacrovar() { struct MacroArg* n = NULL; if (!(n = GetHead(&argstack))) { /* this function can not work if not called from within a macrocall */ abort2(); } /* if */ DelVarFromTree (&n->ma_Locals, av[1]); } /* do_unsetmacrovar */ /* *! >RET *! Terminate the current macro *! */ void do_retmacro (void) { MacroBreak = globalflags.Abortcommand ? 2 : 1; globalflags.Abortcommand = 1; } /* do_retmacro */ /* *! >SETMACRO macroname numargs body *! create a macro definition *! please note that a macro-body is read twice once on definition *! and once on execution (variables should be excaped so) *! *! after such a definition it is possible to use macroname like *! any DME-command; the following numargs expressions are used *! as the macros variables, they can be accessed for read with *! $arg0-$arg<numargs> (not for write)($arg0 returnes the macro's *! name) *! *! BUG: if numargs is not a number then this command *! will assume a value of "0" *! *! WARNING: the synopsis of that command MIGHT change in near future, as *! I am thinking about use of macros' local flags or static vars *! so there might come up another number of local-flags -> *! SETMACRO name numargs NUMFLAGS|LIST-OF-NAMES body *! */ void do_setmacro() { /* printf ("%d\n", */ SetMacro(av[1], av[3], atoi(av[2])); } /* do_setmacro */ /* *! >UNSETMACRO name *! remove a macro definition *! */ void do_unsetmacro() { DelMacro(av[1]); } /* do_unsetmacro */ /* *! >MACROSAVE filename *! save the current macros into a file of a special format *! */ void do_macrosave (void) { FILE * fo = NULL; if ((fo = fopen(av[1], "w"))) { savemacros(fo); fclose(fo); } else { error ("%s:\nCan't open file %s for output", av[0], av[1]); abort2(); } /* if */ } /* do_macrosave */ /* *! >MACROLOAD filename *! read a special macrofile *! */ void do_macroload (void) { FILE * fi = NULL; int lineno = 0; if ((fi = fopen(av[1], "r"))) { loadmacros(fi, &lineno); fclose(fi); } else { error ("%s:\nCan't open file %s for input", av[0], av[1]); globalflags.Abortcommand = 1; } /* if */ } /* do_macrosave */ #ifdef STATIC_COM void* lockmvar (char *name) { if (!(n = GetHead(&argstack))) { /* this function can not work if not called from within a macrocall */ return(NULL); } /* if */ return(AVL_Find (&n->ma_Locals, name)); } /* lockmvar */ char *macrobody(MACRO *handle) { return handle->body; } #endif #ifdef PATCH_NULL /* PATCH_NULL < 24-06-94 */ /* ************************************************** STATIC XDME Command Interface ************************************************** // // Name #Args Function Flags // Name Pri Lock Unlock Call NArgs :FX:COMTREE maro 1 getmacro return0 callmacro nummacroargs // Name Pri Lock Unlock Get Set Unset :FX:VARTREE marg 1 getmacroarg free retval return0 return0 :FX:VARTREE mvar 1 lockmvar return0 nodeget nodeset return0 :FX:VARTREE mbody -1 getmacro return0 macrobody return0 return0 // ************************************************** DYNAMIC XDME Command Interface ************************************************** */ /* ** ALIAS_init () ** ** Init All Macro-Stuff ** init all name-lists ** init the arg-stack ** enable the builtin macros (if macros are global) ** ** That function has to be called at programstart ** (that is done by the line below) */ static const struct CommandNode ALIAS_Commands[] = { { ENODE("setmacro"), 3, CF_VWM|CF_ICO|CF_COK, (FPTR)do_setmacro }, { ENODE("unsetmacro"), 1, CF_VWM|CF_ICO|CF_COK, (FPTR)do_unsetmacro }, { ENODE("ret"), 0, CF_VWM|CF_ICO|CF_COK, (FPTR)do_retmacro }, { ENODE("smv"), 2, CF_VWM|CF_ICO|CF_COK, (FPTR)do_setmacrovar }, { ENODE("setmacrovar"), 2, CF_VWM|CF_ICO|CF_COK, (FPTR)do_setmacrovar }, { ENODE("unsetmacrovar"), 1, CF_VWM|CF_ICO|CF_COK, (FPTR)do_unsetmacrovar }, { ENODE("macroload"), 1, CF_VWM|CF_ICO, (FPTR)do_macroload }, /* tester */ { ENODE("macrosave"), 1, CF_VWM|CF_ICO, (FPTR)do_macrosave } /* tester */ }; MK_AUTOINIT( ALIAS_Init ) { int i; /* ---- init the local structures */ macros = NULL; delenda = NULL; NewList ((LIST*)&argstack); #ifndef PATCH_PACK GetDefMacros (); #endif /* PATCH_PACK */ for (i = sizeof (ALIAS_Commands)/sizeof (struct CommandNode) - 1;i >= 0; --i) { COM_Add (&ALIAS_Commands[i]); } /* for */ } /* ALIAS_Init */ #if 0 MK_AUTOEXIT( ALIAS_Exit ) { int i; APTR lock; for (i = sizeof (ALIAS_Commands)/sizeof (struct CommandNode) - 1;i >= 0; --i) { if (lock = COM_Lock (ALIAS_Commands[i].cn_Name)) COM_Remove (lock); } /* ALIAS_Exit */ #endif #else Prototype int init_macros (void); int init_macros (void) { NewList ((LIST*)&argstack); delenda = NULL; macros = NULL; return GetDefMacros (); } /* init_macros */ #endif /****************************************************************************** ***** ENDE macros.c ******************************************************************************/